import wx, wx.adv
from wx.lib.wordwrap import wordwrap
import os
import matplotlib.pyplot as plt
import matplotlib.colors as colors
from wordcloud import WordCloud
import jieba


class ChooseFrame(wx.Frame):
    def __init__(self, log):
        wx.Frame.__init__(self, None, -1, u'自定义词云工具', size=(520, 580),
                          style=wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.RESIZE_BORDER)
        self.log = log
        self.Center()   # 生成界面在屏幕居中
        self.SetBackgroundColour('white')   # 设置背景色
        font = wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False)     # 字体规格
        self.SetFont(font)  # 应用
        self.mb = self.make_menu_bar()  # 创建菜单栏
        sizer = wx.BoxSizer(wx.VERTICAL)
        texts = ['选择文本', '选择图片', '删除词汇']
        self.addbox(sizer, texts)
        box1 = wx.BoxSizer(wx.HORIZONTAL)
        box1.Add(wx.StaticText(self, -1, "    "))
        cb = wx.CheckBox(self, -1, "分词     ")
        self.Bind(wx.EVT_CHECKBOX, self.cut_or_not, cb)
        box1.Add(cb, 0, wx.ALIGN_CENTRE | wx.ALL, 5)
        font_style = ['楷体', '等线', '黑体', '微软雅黑', '宋体', '仿宋', 'Arial', 'Calibri']
        box1.Add(wx.StaticText(self, -1, "字体"), 0, wx.ALIGN_CENTRE | wx.ALL, 5)
        self.ch = wx.Choice(self, -1, choices=font_style)
        self.ch.Select(0)   # 默认楷体
        box1.Add(self.ch, 0, wx.ALIGN_CENTRE | wx.ALL, 5)
        self.Bind(wx.EVT_CHOICE, self.choose_font, self.ch)
        sizer.Add(box1)
        box2 = wx.BoxSizer(wx.HORIZONTAL)
        btn_create = wx.Button(self, -1, '生成', size=(70, -1))  # 设置按钮
        self.Bind(wx.EVT_BUTTON, self.create, btn_create)
        box2.Add(btn_create, 0, wx.ALIGN_CENTRE | wx.ALL, 5)
        self.panel = wx.Panel(self, wx.ID_ANY, style=wx.BORDER_DOUBLE, size=wx.Size(350, 350))
        self.panel.SetBackgroundColour('#00ff68')
        wx.StaticText(self.panel, -1, "预览区")
        box2.Add(self.panel, 0, wx.ALIGN_CENTRE | wx.ALL, 5)
        sizer.Add(box2, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
        self.SetSizer(sizer)
        sizer.Fit(self)
        self.cut_flag = False
        self.font = '楷体'

    # 创建菜单栏
    def make_menu_bar(self):
        menubar = wx.MenuBar()  # 创建菜单栏
        self.SetMenuBar(menubar)  # 添加到窗口
        file_menu = wx.Menu()  # 创建一个菜单
        menubar.Append(file_menu, '关于')
        self.Center()
        file_menu.AppendSeparator()  # 分割线
        menu_save = file_menu.Append(id=wx.ID_NEW, item='软件简介')  # 往菜单中添加一个菜单项
        self.Bind(wx.EVT_MENU, self.about, menu_save)  # 为此菜单项添加事件处理

    def about(self, event):
        info = wx.adv.AboutDialogInfo()
        info.Version = "1.0"
        info.Description = wordwrap(
            "关于文件选择或直接输入提示：\n\n"
            "1.文本选择可一次性选取多个或多次选取,若手动输入多个路径,需在路径间以英文分号(;)隔开\n\n"
            "2.图片选择只能选用一张背景图作为词云外形,若有多个路径只会选取第一个,尽量采用jpg格式图片\n\n"
            "3.删除词汇的文件若不是txt格式可能无法读取,文件中每个删除词占一行,停用词不多时可在输入框手动输入,同样以英文分号隔开\n\n",
            650, wx.ClientDC(self))
        info.Developers = ['lbf']
        info.License = wordwrap("blah  " * 3, 500, wx.ClientDC(self))
        wx.adv.AboutBox(info)

    def show_message(self, word=""):
        dlg = wx.MessageDialog(None, word, u"错误", wx.YES_NO | wx.ICON_QUESTION)
        if dlg.ShowModal() == wx.ID_YES:
            # self.Close(True)
            pass
        dlg.Destroy()

    def addbox(self, sizer, texts):
        for text in texts:
            box = wx.BoxSizer(wx.HORIZONTAL)
            btn = wx.Button(self, -1, text)
            box.Add(btn, 0, wx.ALIGN_CENTRE | wx.ALL, 5)
            if '文本' in text:
                self.entry_txt = wx.TextCtrl(self, -1, "", size=(250, -1))
                box.Add(self.entry_txt, 1, wx.ALIGN_CENTRE | wx.ALL, 5)
                self.Bind(wx.EVT_BUTTON, self.choose_txt, btn)
            elif '图片' in text:
                self.entry_img = wx.TextCtrl(self, -1, "", size=(250, -1))
                box.Add(self.entry_img, 1, wx.ALIGN_CENTRE | wx.ALL, 5)
                self.Bind(wx.EVT_BUTTON, self.choose_img, btn)
            else:
                self.entry_stop = wx.TextCtrl(self, -1, "", size=(250, -1))
                box.Add(self.entry_stop, 1, wx.ALIGN_CENTRE | wx.ALL, 5)
                self.Bind(wx.EVT_BUTTON, self.choose_stop, btn)
            sizer.Add(box, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)

    def choose_txt(self, event):
        wildcard = "文本文档 (*.txt)|*.txt"
        dlg = wx.FileDialog(
            self, message="选择",
            defaultDir=os.getcwd(),
            defaultFile="",
            wildcard=wildcard,
            style=wx.FD_OPEN | wx.FD_MULTIPLE |
                  wx.FD_CHANGE_DIR | wx.FD_FILE_MUST_EXIST |
                  wx.FD_PREVIEW
        )
        if dlg.ShowModal() == wx.ID_OK:
            paths = dlg.GetPaths()
            if self.entry_txt.GetValue():
                self.entry_txt.write(';' + ';'.join(paths))
            else:
                self.entry_txt.write(';'.join(paths))
            # for path in paths:
            #     self.log.WriteText('         %s\n' % path)
        dlg.Destroy()

    def choose_img(self, event):
        wildcard = "图片 (*.jpg;*.png)|*.jpg;*.png"
        dlg = wx.FileDialog(
            self, message="选择",
            defaultDir=os.getcwd(),
            defaultFile="",
            wildcard=wildcard,
            style=wx.FD_OPEN | wx.FD_MULTIPLE |
                  wx.FD_CHANGE_DIR | wx.FD_FILE_MUST_EXIST |
                  wx.FD_PREVIEW
        )
        if dlg.ShowModal() == wx.ID_OK:
            paths = dlg.GetPaths()
            if self.entry_img.GetValue():
                self.entry_img.Clear()
            self.entry_img.write(paths[0])
            # for path in paths:
            #     self.log.WriteText('         %s\n' % path)
        dlg.Destroy()

    def choose_stop(self, event):
        wildcard = "文本文档 (*.txt)|*.txt"
        dlg = wx.FileDialog(
            self, message="选择",
            defaultDir=os.getcwd(),
            defaultFile="",
            wildcard=wildcard,
            style=wx.FD_OPEN | wx.FD_MULTIPLE |
                  wx.FD_CHANGE_DIR | wx.FD_FILE_MUST_EXIST |
                  wx.FD_PREVIEW
        )
        if dlg.ShowModal() == wx.ID_OK:
            paths = dlg.GetPaths()
            if self.entry_stop.GetValue():
                self.entry_stop.write(';' + ';'.join(paths))
            else:
                self.entry_stop.write(';'.join(paths))
            # for path in paths:
            #     self.log.WriteText('         %s\n' % path)
        dlg.Destroy()

    def cut_or_not(self, event):
        self.cut_flag = event.IsChecked()

    def choose_font(self, event):
        self.font = event.GetString()

    def create(self, event):
        txt_path = self.entry_txt.GetValue().split(';')
        stop_path = self.entry_stop.GetValue().split(';')
        img_path = self.entry_img.GetValue()
        text = ''
        for txt in txt_path:
            try:
                with open(txt, 'r', encoding='gbk') as f:
                    text += f.read()
            except UnicodeDecodeError:
                if txt.endswith('.txt'):
                    with open(txt, 'r', encoding='utf8') as f:
                        text += f.read()
                else:
                    self.log.WriteText('请使用正确的文本路径，文件后缀为.txt')
                    return
            except FileNotFoundError:
                self.log.WriteText('请使用正确的文本路径，相对路径也可以')
                return
        words = set()
        for word in stop_path:
            try:
                with open(word, 'r', encoding='gbk') as f:
                    words.update(set(f.read().split('\n')))
            except UnicodeDecodeError:
                with open(word, 'r', encoding='utf8') as f:
                    words.update(set(f.read().split('\n')))
            except:
                words.add(word)
        self.word_cloud(text, img_path, words)

    def word_cloud(self, text, img_path, words):
        if self.cut_flag:
            text = ' '.join(jieba.cut(text))
        try:
            mask = plt.imread(img_path)
        except AttributeError:
            mask = None
        color_list = ['#CD853F', '#DC143C', '#00FF7E', '#FF6347', '#8B008B', '#00FFFF', '#8B0000', '#FF8C00',
                      '#1E90FF', '#00FF00', '#FFD700', '#008080', '#008B8B', '#8A2BE2', '#228B22', '#FA8072',
                      '#808080']
        colormap = colors.ListedColormap(color_list)
        dic_font = {
            '楷体': r'C:\Windows\Fonts\simkai.ttf',
            '等线': r'C:\Windows\Fonts\Deng.ttf',
            '仿宋': r'C:\Windows\Fonts\simfang.ttf',
            '黑体': r'C:\Windows\Fonts\simhei.ttf',
            '宋体': r'C:\Windows\Fonts\simsun.ttc',
            '微软雅黑': r'C:\Windows\Fonts\msyhbd.ttc',
            'Arial': r'C:\Windows\Fonts\arial.ttf',
            'Calibri': r'C:\Windows\Fonts\calibri.ttf',
        }
        wc = WordCloud(
            background_color='white',
            mode='RGBA',
            mask=mask,
            colormap=colormap,  # 自定义字体颜色对象
            width=1000,
            height=1000,
            random_state=30,
            relative_scaling=0.5,
            prefer_horizontal=0.7,  # 词语水平方向排版出现的频率（默认0.9）
            min_font_size=8,
            max_font_size=300,
            font_path=dic_font[self.font],  # 若是有中文的话，这句代码必须添加，不然会出现方框，不出现汉字
            max_words=1000,
            stopwords=words
        )
        wc.generate_from_text(text)
        # wc.generate_from_frequencies(dic)
        plt.figure(figsize=(10,10))
        plt.imshow(wc)
        plt.axis('off')
        plt.savefig('词云.jpg', dpi=300)
        image = wx.Image('1词云.jpg', wx.BITMAP_TYPE_ANY).Rescale(350, 350)
        wx.StaticBitmap(self.panel, wx.ID_ANY, wx.Bitmap(image, wx.BITMAP_SCREEN_DEPTH))


class Log:
    def WriteText(self, text):
        if text[-1:] == '\n':
            text = text[:-1]
        wx.LogMessage(text)
    write = WriteText


if __name__ == "__main__":
    app = wx.App()
    log = Log()
    frame = ChooseFrame(log)
    frame.Show()
    app.MainLoop()
